package store.lunangangster.nodeflow.rt;

import store.lunangangster.nodeflow.common.ResponseCode;
import store.lunangangster.nodeflow.config.NodeFlowConfig;
import store.lunangangster.nodeflow.core.NodeFlowResponse;
import store.lunangangster.nodeflow.exception.ChainNotFoundException;
import store.lunangangster.nodeflow.rt.element.Chain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Chain流程的执行器
 *
 * @author cwk
 * @version 1.0
 */
public class ChainExecutor {

	private static final Logger log = LoggerFactory.getLogger(ChainExecutor.class);

	private final NodeFlowConfig nodeFlowConfig;

	/**
	 * Chain执行器的简单构造器。并不建议用户通过该构造方法来获取一个ChainExecutor，而是通过{@link NodeFlowRuntime#loadConfig(NodeFlowConfig)}方法来
	 * 加载{@link NodeFlowConfig}并且获取{@link ChainExecutor}
	 * @param nodeFlowConfig	系统配置对象
	 */
	public ChainExecutor(NodeFlowConfig nodeFlowConfig) {
		this.nodeFlowConfig = nodeFlowConfig;
	}

	/**
	 * 执行指定id的chain流程
	 * @param chainId	待执行的Chain的id
	 * @return NodeFlowResponse
	 */
	public NodeFlowResponse exec(String chainId) {
		try {
			if (needInit()) {
				if (!nodeFlowConfig.getParseOnStart()) {
					NodeFlowRuntime.init();
				} else {
					return NodeFlowResponse.getInstance()
							.success(false)
							.status(ResponseCode.SYSTEM_NOT_INIT);
				}
			}
		} catch (Exception cause) {
			return NodeFlowResponse.getInstance()
					.success(false)
					.message(cause.getMessage())
					.cause(cause);
		}
		Integer slotIndex = DataBus.nextSlot();
		log.info("Chain[{}] is assigned executing in Slot[{}]", chainId, slotIndex);
		try {
			return this.exec(chainId, slotIndex);
		} catch (Exception cause) {
			cause.printStackTrace();
			return NodeFlowResponse.getInstance()
					.success(false)
					.message(cause.getMessage())
					.cause(cause);
		} finally {
			DataBus.freeSlot(slotIndex);
			log.info("Slot[{}] is set free", slotIndex);
			NodeFlowRuntime.setInit();
		}
	}

	/**
	 * 执行指定id的chain流程，使用slotIndex处的Slot，见{@link Slot}
	 * @param chainId	待执行的Chain的id
	 * @param slotIndex	被分配的{@link Slot}下标
	 * @return NodeFlowResponse
	 */
	private NodeFlowResponse exec(String chainId, Integer slotIndex) {
		// 解析
		Chain chain = FlowBus.getChain(chainId);
		if (chain == null) {
			return NodeFlowResponse.getInstance()
					.success(false)
					.status(ResponseCode.CHAIN_NOT_FOUND)
					.cause(new ChainNotFoundException("chainId: %s, is not defined in Rule Source"));
		}
		chain.execute(slotIndex);
		return NodeFlowResponse.getInstance()
				.success(true)
				.status(ResponseCode.SUCCESS);
	}

	/**
	 * 该方法用于系统开始之前NodeFlow是否需要初始化的判断。在第一次初始化完成之后，
	 * 即使该方法返回false，也可以进行初始化操作
	 * @return true如果需要进行初始化
	 */
	private boolean needInit() {
		return NodeFlowRuntime.needInit();
	}
}
